package com.jnc.pay.biz.common.work;

import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.core.util.XmlUtil;
import com.alipay.api.AlipayApiException;
import com.alipay.api.internal.util.AlipaySignature;
import com.jnc.pay.biz.common.mapper.*;
import com.jnc.pay.biz.common.model.*;
import com.jnc.pay.biz.common.service.WechatpayConfigService;
import com.jnc.pay.constant.AliPayConstant;
import com.jnc.pay.constant.BizConstant;
import com.jnc.pay.constant.EventConstant;
import com.jnc.pay.constant.WechatPayConstant;
import com.jnc.pay.core.model.BaseResp;
import com.jnc.pay.core.model.RespCode;
import com.jnc.pay.core.vertx.verticle.MultiThreadedWorkerVerticle;
import com.jnc.pay.util.convert.ConvertUtil;
import com.jnc.pay.util.convert.JsonUtil;
import com.jnc.pay.util.gen.IdGen;
import com.jnc.pay.util.pay.SignUtil;
import com.jnc.pay.util.secret.WechatAESUtil;
import io.vertx.core.AsyncResult;
import io.vertx.core.Handler;
import io.vertx.core.buffer.Buffer;
import io.vertx.ext.web.client.HttpResponse;
import io.vertx.ext.web.client.WebClient;
import io.vertx.ext.web.client.WebClientOptions;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import javax.annotation.PreDestroy;
import javax.annotation.Resource;
import java.security.Security;
import java.util.Map;

/**
 * @Auther: jjn
 * @Date: 2020/7/28
 * @Desc: 异步回调work
 */
@Slf4j
@Order(1)
@Component
public class NotifyWork extends MultiThreadedWorkerVerticle {

    @Autowired
    private WebClientOptions webClientOptions;
    private WebClient webClient;
    @Resource
    TradeMapper tradeMapper;
    @Resource
    RefundMapper refundMapper;
    @Resource
    NotifyMapper notifyMapper;
    @Resource
    ContractMapper contractMapper;
    @Resource
    AlipayConfigMapper alipayConfigMapper;
    @Autowired
    WechatpayConfigService wechatpayConfigService;
    @Resource
    WechatpayConfigMapper wechatpayConfigMapper;

    private long timeout = 5000;
    private static final String CONTRACT_ADD = "ADD"; //签约
    private static final String CONTRACT_DELETE = "DELETE"; //解约


    @Override
    public void start() throws Exception {

        webClient = WebClient.create(vertx, webClientOptions);

        //微信支付异步通知
        vertx.eventBus().consumer(EventConstant.EVENT_WX_PAY_CALLBACK_NOTIFY, event -> {
            String xmlStr = (String) event.body();
            Map<String, Object> map = XmlUtil.xmlToMap(xmlStr);
            if(MapUtil.isEmpty(map)){
                log.warn("wechat pay callback error, reqeust params is null");
                return;
            }
            //支付异步通知处理
            Long out_trade_no = MapUtil.getLong(map, "out_trade_no");
            Trade trade = tradeMapper.getTradeById(out_trade_no);
            if(trade != null){
                editWechatTrade(trade, map);
            }
        });

        //微信退款异步通知
        vertx.eventBus().consumer(EventConstant.EVENT_WX_REFUND_CALLBACK_NOTIFY, event -> {
            String xmlStr = (String) event.body();
            Map<String, Object> map = XmlUtil.xmlToMap(xmlStr);
            if(MapUtil.isEmpty(map)){
                log.warn("wechat refund callback error, reqeust params is null");
                return;
            }
            //退款异步通知处理
            editWechatRefund(map);
        });

        //微信签约、解约异步通知
        vertx.eventBus().consumer(EventConstant.EVENT_WX_CONTRACT_CALLBACK_NOTIFY, event -> {
            String xmlStr = (String) event.body();
            Map<String, Object> map = XmlUtil.xmlToMap(xmlStr);
            if(MapUtil.isEmpty(map)){
                log.warn("wechat contract and termination callback error, reqeust params is null");
                return;
            }
            String contract_code = MapUtil.getStr(map, "contract_code");
            Contract model = contractMapper.getContractByCode(contract_code);
            if(model != null){
                //签约、解约异步通知处理
                editWechatContract(map, model);
            }
        });

        //微信代扣异步通知
        vertx.eventBus().consumer(EventConstant.EVENT_WX_WITHHOLD_CALLBACK_NOTIFY, event -> {
            String xmlStr = (String) event.body();
            Map<String, Object> map = XmlUtil.xmlToMap(xmlStr);
            if(MapUtil.isEmpty(map)){
                log.warn("wechat withhold callback error, reqeust params is null");
                return;
            }
            //代扣异步通知处理
            Long out_trade_no = MapUtil.getLong(map, "out_trade_no");
            Trade trade = tradeMapper.getTradeById(out_trade_no);
            if(trade != null){
                editWechatWithholdTrade(trade, map);
            }
        });

        //支付宝支付、退款异步通知
        vertx.eventBus().consumer(EventConstant.EVENT_ALI_CALLBACK_NOTIFY, event -> {
            Map<String, String> map = (Map<String, String>) event.body();
            if(MapUtil.isEmpty(map)){
                log.warn("Alipay callback error, reqeust params is null");
                return;
            }
            //支付、退款异步通知处理
            editAliTradeAndRefund(map);
        });
    }

    /**
     * 更新微信支付订单信息，并通知到第三方
     * @param trade
     * @param map
     */
    private void editWechatTrade(Trade trade, Map<String, Object> map){
        Map<String, Object> respMap = MapUtil.newHashMap();
        BaseResp<Map<String, Object>> resp = new BaseResp<>();
        String sign = MapUtil.getStr(map, "sign");
        int total_fee = MapUtil.getInt(map, "total_fee") == null ? 0 : MapUtil.getInt(map, "total_fee").intValue();
        //判断支付订单金额是否匹配
        if(trade.getTotalFee().intValue() != total_fee){
            log.warn("wechatpay pay callback error, The total_fee does not match, db total_fee: {}, callback total_fee: {}", trade.getTotalFee(), total_fee);
            return;
        }
        //验签
        WechatpayConfig wechatpayAppConfig = wechatpayConfigService.getWechatpayAppConfig(trade.getAppId());
        if(wechatpayAppConfig != null){
            String genSign = SignUtil.getSign(map, wechatpayAppConfig.getApiSecret(), "sign");
            if(!StrUtil.equals(sign, genSign)){
                log.warn("wechatpay pay callback error, The sign does not match, gen sign: {}, callback sign: {}", genSign, sign);
                return;
            }
        }
        //解析回调参数
        String return_code = MapUtil.getStr(map, "return_code");
        String result_code = MapUtil.getStr(map, "result_code");
        String err_code = MapUtil.getStr(map, "err_code");
        String err_code_des = MapUtil.getStr(map, "err_code_des");
        String transaction_id = MapUtil.getStr(map, "transaction_id");
        long time_end = DateUtil.parse(MapUtil.getStr(map, "time_end"), DatePattern.PURE_DATETIME_PATTERN).getTime() / 1000;

        //组装支付订单信息
        Trade editTrade = new Trade();
        editTrade.setTradeId(trade.getTradeId());
        editTrade.setPaymentTime((int) time_end);
        editTrade.setTradeNo(transaction_id);
        editTrade.setOldOrderStatus(BizConstant.WAIT_PAY); //原始订单状态为：0 待支付
        if(WechatPayConstant.SUCCESS_CODE.equals(return_code) && WechatPayConstant.SUCCESS_CODE.equals(result_code)){
            editTrade.setOrderStatus(BizConstant.PAY_SUCCESS);
            resp.setCode(RespCode.SUCCESS.getCode()).setMsg(RespCode.SUCCESS.getDesc());
        }else{
            editTrade.setOrderStatus(BizConstant.PAY_FAIL);
            editTrade.setErorrCode(err_code);
            editTrade.setErorrMsg(err_code_des);
            resp.setCode(RespCode.ERROR.getCode()).setMsg(err_code_des);
        }
        //更新订单状态
        log.debug("Update wechat pay order status, model: {}", JsonUtil.bean2Json(editTrade));
        tradeMapper.editTrade(editTrade);

        //封装通知记录
        Notify notify = new Notify();
        long notifyId = IdGen.getId();
        notify.setNotifyId(notifyId);
        notify.setAppId(trade.getAppId());
        notify.setTradeId(trade.getTradeId());
        notify.setAppOrderId(trade.getAppOrderId());
        notify.setNotifyType(BizConstant.NOTIFY_TYPE_PAY);
        notify.setNotifyUrl(trade.getNotifyUrl());
        notify.setNotifyCount(1);
        notify.setNotifyStatus(BizConstant.NOTIFY_PENDING);
        log.debug("Add wechat pay notify record, model: {}", notify);
        notifyMapper.addNotify(notify);

        //通知商户
        respMap.put("app_order_id", trade.getAppOrderId());
        respMap.put("total_fee", total_fee);
        respMap.put("trade_id", trade.getTradeId());
        resp.setData(respMap);
        String json = JsonUtil.bean2Json(resp);
        log.debug("wechat pay notify mch, url: {}, req: {}", trade.getNotifyUrl(), json);
        notify(trade.getNotifyUrl(), json, ar -> {
            Notify dto = new Notify();
            dto.setNotifyId(notifyId);
            dto.setNotifyCount(1);
            dto.setNotifyStatus(BizConstant.NOTIFY_FAIL);
            if(ar.succeeded()){
                int code = ar.result().bodyAsJsonObject().getInteger("code");
                if(code == 0){
                    Trade tradeNotify = new Trade();
                    tradeNotify.setTradeId(trade.getTradeId());
                    tradeNotify.setOrderStatus(BizConstant.PAY_BIZ_COMPLETE);
                    tradeNotify.setFinishTime((int) (System.currentTimeMillis()/1000));
                    tradeNotify.setOldOrderStatus(BizConstant.PAY_SUCCESS); //原始订单状态为：1 支付成功
                    log.debug("Wechat pay notification mch success, model: {}", JsonUtil.bean2Json(tradeNotify));
                    tradeMapper.editTrade(tradeNotify);

                    dto.setNotifyStatus(BizConstant.NOTIFY_SUCCESS);
                }
            }
            log.debug("Update wechat pay notify record, model: {}", dto);
            notifyMapper.editNotify(dto);
        });
    }

    /**
     * 更新微信退款订单信息，并通知到第三方
     * @param map
     */
    private void editWechatRefund(Map<String, Object> map){
        Map<String, Object> respMap = MapUtil.newHashMap();
        //获取核心参数
        String return_code = MapUtil.getStr(map, "return_code");
        String result_code = MapUtil.getStr(map, "result_code");
        String appid = MapUtil.getStr(map, "appid");
        String mch_id = MapUtil.getStr(map, "mch_id");
        String req_info = MapUtil.getStr(map, "req_info");

        //获取微信商户配置
        WechatpayConfig config = new WechatpayConfig();
        config.setWechatpayAppId(appid);
        config.setMerchantId(mch_id);
        WechatpayConfig wechatpayConfig = wechatpayConfigMapper.queryConfigBy(config);
        if(wechatpayConfig == null || req_info == null){
            log.warn("wechat refund callback error, no valid wechatpay config");
            return;
        }
        
        //解密响应参数
        Security.addProvider(WechatAESUtil.getProviderInstance());
        String xmlReq = WechatAESUtil.decrypt(req_info, wechatpayConfig.getApiSecret());
        log.debug("req_info decrypt: {}", xmlReq);
        Map<String, Object> data = XmlUtil.xmlToMap(xmlReq);
        Long out_refund_no = ConvertUtil.toLong(MapUtil.getStr(data, "out_refund_no"));
        Refund refund = refundMapper.getRefundById(out_refund_no);


        BaseResp<Map<String, Object>> resp = new BaseResp<>();
        if(refund != null){
            long time_end = DateUtil.parse(MapUtil.getStr(data, "success_time"), DatePattern.NORM_DATETIME_PATTERN).getTime() / 1000;
            String refund_status = MapUtil.getStr(data, "refund_status");
            String refund_id = MapUtil.getStr(data, "refund_id"); //微信退款单号

            //组装退款
            Refund editRefund = new Refund();
            editRefund.setRefundId(refund.getRefundId());
            editRefund.setRefundTime((int) time_end);
            editRefund.setRefundNo(refund_id);
            editRefund.setOldRefundStatus(BizConstant.WAIT_REFUND); //原始退款状态为：0 待退款
            if(WechatPayConstant.SUCCESS_CODE.equals(return_code) && WechatPayConstant.SUCCESS_CODE.equals(result_code)){
                if(StrUtil.equals(WechatPayConstant.SUCCESS_CODE, refund_status)){ //退款成功
                    editRefund.setRefundStatus(BizConstant.REFUND_SUCCESS);
                    resp.setCode(RespCode.SUCCESS.getCode()).setMsg(RespCode.SUCCESS.getDesc());
                }else{
                    editRefund.setRefundStatus(BizConstant.REFUND_FAIL);
                    editRefund.setErorrCode(refund_status);
                    resp.setCode(RespCode.ERROR.getCode()).setMsg(refund_status);
                }
            }else{
                editRefund.setRefundStatus(BizConstant.REFUND_FAIL);
                editRefund.setErorrCode(refund_status);
                resp.setCode(RespCode.ERROR.getCode()).setMsg(refund_status);
            }
            //更新退款订单状态
            log.debug("Update wechat refund order status, model: {}", JsonUtil.bean2Json(editRefund));
            refundMapper.editRefund(editRefund);

            //封装通知记录
            Notify notify = new Notify();
            long notifyId = IdGen.getId();
            notify.setNotifyId(notifyId);
            notify.setAppId(refund.getAppId());
            notify.setTradeId(refund.getTradeId());
            notify.setAppOrderId(refund.getAppRefundId());
            notify.setNotifyType(BizConstant.NOTIFY_TYPE_REFUND);
            notify.setNotifyUrl(refund.getNotifyUrl());
            notify.setNotifyCount(1);
            notify.setNotifyStatus(BizConstant.NOTIFY_PENDING);
            log.debug("Add wechat refund notify record, model: {}", JsonUtil.bean2Json(notify));
            notifyMapper.addNotify(notify);

            //通知商户
            respMap.put("app_refund_id", refund.getAppRefundId());
            respMap.put("refund_fee", refund.getRefundFee());
            respMap.put("trade_id", refund.getTradeId());
            respMap.put("refund_id", refund.getRefundId());
            resp.setData(respMap);
            String json = JsonUtil.bean2Json(resp);
            log.debug("wechat refund notify mch, url: {}, req: {}", refund.getNotifyUrl(), json);
            notify(refund.getNotifyUrl(), json, ar -> {
                Notify dto = new Notify();
                dto.setNotifyId(notifyId);
                dto.setNotifyCount(1);
                dto.setNotifyStatus(BizConstant.NOTIFY_FAIL);
                if(ar.succeeded()){
                    int code = ar.result().bodyAsJsonObject().getInteger("code");
                    if(code == 0){
                        Refund refundNotify = new Refund();
                        refundNotify.setTradeId(refund.getRefundId());
                        refundNotify.setRefundStatus(BizConstant.REFUND_BIZ_COMPLETE);
                        refundNotify.setFinishTime((int) (System.currentTimeMillis()/1000));
                        refundNotify.setOldRefundStatus(BizConstant.REFUND_SUCCESS); //原始退款状态为：1 退款成功
                        log.debug("Wechat refund notification mch success, model: {}", JsonUtil.bean2Json(refundNotify));
                        refundMapper.editRefund(refundNotify);

                        dto.setNotifyStatus(BizConstant.NOTIFY_SUCCESS);
                    }
                }
                log.debug("Update wechat refund notify record, model: {}", dto);
                notifyMapper.editNotify(dto);
            });
        }
    }

    /**
     * 更新微信签约、解约信息，并通知到第三方
     * @param map
     */
    private void editWechatContract(Map<String, Object> map, Contract model){
        Map<String, Object> respMap = MapUtil.newHashMap();
        BaseResp<Map<String, Object>> resp = new BaseResp<>();
        String sign = MapUtil.getStr(map, "sign");
        //验签
        WechatpayConfig wechatpayAppConfig = wechatpayConfigService.getWechatpayAppConfig(model.getAppId());
        if(wechatpayAppConfig != null){
            String genSign = SignUtil.getSign(map, wechatpayAppConfig.getApiSecret(), "sign");
            if(!StrUtil.equals(sign, genSign)){
                log.warn("wechatpay contract and termination callback error, The sign does not match, gen sign: {}, callback sign: {}", genSign, sign);
                return;
            }
        }

        //解析回调参数
        String return_code = MapUtil.getStr(map, "return_code");
        String result_code = MapUtil.getStr(map, "result_code");
        String change_type = MapUtil.getStr(map, "change_type");
        String err_code = MapUtil.getStr(map, "err_code");
        String err_code_des = MapUtil.getStr(map, "err_code_des");
        String contract_id = MapUtil.getStr(map, "contract_id");
        String plan_id = MapUtil.getStr(map, "plan_id");
        //组装签约订单信息
        Contract editContract = new Contract();
        editContract.setContractId(model.getContractId());
        if(StrUtil.equals(change_type, CONTRACT_ADD)){ //签约
            String contract_expired_time = MapUtil.getStr(map, "contract_expired_time");

            respMap.put("contract_expired", contract_expired_time);

            editContract.setContractNo(contract_id);
            editContract.setContractExpire(contract_expired_time);
            if(WechatPayConstant.SUCCESS_CODE.equals(return_code) && WechatPayConstant.SUCCESS_CODE.equals(result_code)){
                editContract.setContractStatus(BizConstant.CONTRACT_SUCCESS);
                resp.setCode(RespCode.SUCCESS.getCode()).setMsg(RespCode.SUCCESS.getDesc());
            }else{
                editContract.setContractStatus(BizConstant.CONTRACT_FAIL);
                editContract.setErorrCode(err_code);
                editContract.setErorrMsg(err_code_des);
                resp.setCode(RespCode.ERROR.getCode()).setMsg(err_code_des);
            }
        }else if(StrUtil.equals(change_type, CONTRACT_DELETE)){ //解约
            Integer contract_termination_mode = MapUtil.getInt(map, "contract_termination_mode");
            editContract.setTerminationMode(contract_termination_mode);
            if(WechatPayConstant.SUCCESS_CODE.equals(return_code) && WechatPayConstant.SUCCESS_CODE.equals(result_code)){
                editContract.setContractStatus(BizConstant.TERMINATION_SUCCESS);
                resp.setCode(RespCode.SUCCESS.getCode()).setMsg(RespCode.SUCCESS.getDesc());
            }else{
                editContract.setContractStatus(BizConstant.TERMINATION_FAIL);
                editContract.setErorrCode(err_code);
                editContract.setErorrMsg(err_code_des);
                resp.setCode(RespCode.ERROR.getCode()).setMsg(err_code_des);
            }
        }else{
            log.warn("Unknown change type: {}", change_type);
            return;
        }

        //更新订单状态
        log.debug("Update wechat contract and termination order status, model: {}", JsonUtil.bean2Json(editContract));
        contractMapper.editContract(editContract);

        //通知商户
        respMap.put("contract_code", model.getContractCode());
        respMap.put("contract_no", contract_id);
        respMap.put("contract_id", model.getContractId());
        respMap.put("plan_id", plan_id);
        resp.setData(respMap);
        String json = JsonUtil.bean2Json(resp);
        log.debug("wechat contract and termination notify mch, url: {}, req: {}", model.getNotifyUrl(), json);
        notify(model.getNotifyUrl(), json, ar -> {
            if(ar.succeeded()){
                int code = ar.result().bodyAsJsonObject().getInteger("code");
                if(code == 0){
                    Contract contractNotify = new Contract();
                    contractNotify.setContractId(model.getContractId());
                    contractNotify.setFinishTime((int) (System.currentTimeMillis()/1000));

                    if(StrUtil.equals(change_type, CONTRACT_ADD)){
                        contractNotify.setContractStatus(BizConstant.CONTRACT_BIZ_COMPLETE);
                        contractNotify.setOldContractStatus(BizConstant.CONTRACT_SUCCESS); //原始订单状态为：1 签约成功
                    }else{
                        contractNotify.setContractStatus(BizConstant.TERMINATION_BIZ_COMPLETE);
                        contractNotify.setOldContractStatus(BizConstant.TERMINATION_SUCCESS); //原始订单状态为：1 解约成功
                    }
                    log.debug("Wechat contract and termination notification mch success, model: {}", JsonUtil.bean2Json(contractNotify));
                    contractMapper.editContract(contractNotify);
                }
            }
        });
    }

    private void editWechatWithholdTrade(Trade trade, Map<String, Object> map){
        Map<String, Object> respMap = MapUtil.newHashMap();
        BaseResp<Map<String, Object>> resp = new BaseResp<>();
        String sign = MapUtil.getStr(map, "sign");
        int total_fee = MapUtil.getInt(map, "total_fee") == null ? 0 : MapUtil.getInt(map, "total_fee").intValue();
        //判断支付订单金额是否匹配
        if(trade.getTotalFee().intValue() != total_fee){
            log.warn("wechatpay withhold callback error, The total_fee does not match, db total_fee: {}, callback total_fee: {}", trade.getTotalFee(), total_fee);
            return;
        }
        //验签
        WechatpayConfig wechatpayAppConfig = wechatpayConfigService.getWechatpayAppConfig(trade.getAppId());
        if(wechatpayAppConfig != null){
            String genSign = SignUtil.getSign(map, wechatpayAppConfig.getApiSecret(), "sign");
            if(!StrUtil.equals(sign, genSign)){
                log.warn("wechatpay withhold callback error, The sign does not match, gen sign: {}, callback sign: {}", genSign, sign);
                return;
            }
        }
        //解析回调参数
        String return_code = MapUtil.getStr(map, "return_code");
        String result_code = MapUtil.getStr(map, "result_code");
        String err_code = MapUtil.getStr(map, "err_code");
        String err_code_des = MapUtil.getStr(map, "err_code_des");
        String transaction_id = MapUtil.getStr(map, "transaction_id");
        String contract_id = MapUtil.getStr(map, "contract_id");
        long time_end = DateUtil.parse(MapUtil.getStr(map, "time_end"), DatePattern.PURE_DATETIME_PATTERN).getTime() / 1000;

        //组装支付订单信息
        Trade editTrade = new Trade();
        editTrade.setTradeId(trade.getTradeId());
        editTrade.setPaymentTime((int) time_end);
        editTrade.setTradeNo(transaction_id);
        editTrade.setOldOrderStatus(BizConstant.WAIT_PAY); //原始订单状态为：0 待支付
        if(WechatPayConstant.SUCCESS_CODE.equals(return_code) && WechatPayConstant.SUCCESS_CODE.equals(result_code)){
            editTrade.setOrderStatus(BizConstant.PAY_SUCCESS);
            resp.setCode(RespCode.SUCCESS.getCode()).setMsg(RespCode.SUCCESS.getDesc());
        }else{
            editTrade.setOrderStatus(BizConstant.PAY_FAIL);
            editTrade.setErorrCode(err_code);
            editTrade.setErorrMsg(err_code_des);
            resp.setCode(RespCode.ERROR.getCode()).setMsg(err_code_des);
        }
        //更新订单状态
        log.debug("Update wechat withhold order status, model: {}", JsonUtil.bean2Json(editTrade));
        tradeMapper.editTrade(editTrade);

        //封装通知记录
        Notify notify = new Notify();
        long notifyId = IdGen.getId();
        notify.setNotifyId(notifyId);
        notify.setAppId(trade.getAppId());
        notify.setTradeId(trade.getTradeId());
        notify.setAppOrderId(trade.getAppOrderId());
        notify.setNotifyType(BizConstant.NOTIFY_TYPE_PAY);
        notify.setNotifyUrl(trade.getNotifyUrl());
        notify.setNotifyCount(1);
        notify.setNotifyStatus(BizConstant.NOTIFY_PENDING);
        log.debug("Add wechat withhold notify record, model: {}", notify);
        notifyMapper.addNotify(notify);

        //通知商户
        respMap.put("app_order_id", trade.getAppOrderId());
        respMap.put("total_fee", total_fee);
        respMap.put("trade_id", trade.getTradeId());
        respMap.put("contract_no", contract_id);
        resp.setData(respMap);
        String json = JsonUtil.bean2Json(resp);
        log.debug("wechat withhold notify mch, url: {}, req: {}", trade.getNotifyUrl(), json);
        notify(trade.getNotifyUrl(), json, ar -> {
            Notify dto = new Notify();
            dto.setNotifyId(notifyId);
            dto.setNotifyCount(1);
            dto.setNotifyStatus(BizConstant.NOTIFY_FAIL);
            if(ar.succeeded()){
                int code = ar.result().bodyAsJsonObject().getInteger("code");
                if(code == 0){
                    Trade tradeNotify = new Trade();
                    tradeNotify.setTradeId(trade.getTradeId());
                    tradeNotify.setOrderStatus(BizConstant.PAY_BIZ_COMPLETE);
                    tradeNotify.setFinishTime((int) (System.currentTimeMillis()/1000));
                    tradeNotify.setOldOrderStatus(BizConstant.PAY_SUCCESS); //原始订单状态为：1 支付成功
                    log.debug("Wechat withhold notification mch success, model: {}", JsonUtil.bean2Json(tradeNotify));
                    tradeMapper.editTrade(tradeNotify);

                    dto.setNotifyStatus(BizConstant.NOTIFY_SUCCESS);
                }
            }
            log.debug("Update wechat withhold notify record, model: {}", dto);
            notifyMapper.editNotify(dto);
        });
    }

    /**
     * 更新支付宝支付、退款订单信息，并通知到第三方
     * @param map
     */
    private void editAliTradeAndRefund(Map<String, String> map){
        Long out_trade_no = ConvertUtil.toLong(MapUtil.getStr(map, "out_trade_no"));
        String refund_fee = MapUtil.getStr(map, "refund_fee");
        Trade trade = tradeMapper.getTradeById(out_trade_no);
        if(trade != null){
            AlipayConfig alipayAppConfig = alipayConfigMapper.getAlipayAppConfig(trade.getAppId());
            boolean flag = false;
            try {
                flag = AlipaySignature.certVerifyV2(map, alipayAppConfig.getPublicKey(), AliPayConstant.CHARSET_TYPE, alipayAppConfig.getSignType());
            } catch (AlipayApiException e) {
                log.debug("Alipay verify exception, map: {}, error: {}", JsonUtil.bean2Json(map), e);
            }
            //验签通过
            if(flag){
                //支付回调
                if(StrUtil.isBlank(refund_fee)){
                    editAliTrade(trade, map);
                }else{ //退款回调
                    editAliRefund(map);
                }
            }else{
                log.warn("Alipay verify failed");
            }
        }
    }

    /**
     * 更新支付宝支付数据
     * @param trade
     * @param map
     */
    private void editAliTrade(Trade trade, Map<String, String> map){
        BaseResp<Map<String, Object>> resp = new BaseResp<>();
        long time_end = DateUtil.parse(MapUtil.getStr(map, "gmt_payment"), DatePattern.NORM_DATETIME_PATTERN).getTime() / 1000;
        String trade_no = MapUtil.getStr(map, "trade_no");
        String trade_status = MapUtil.getStr(map, "trade_status");
        //组装支付订单信息
        Trade editTrade = new Trade();
        editTrade.setTradeId(trade.getTradeId());
        editTrade.setPaymentTime((int) time_end);
        editTrade.setTradeNo(trade_no);
        editTrade.setOldOrderStatus(BizConstant.WAIT_PAY); //原始订单状态为：0 待支付
        if(StrUtil.equals(AliPayConstant.TRADE_STATUS_SUCCESS, trade_status)
                && StrUtil.equals(AliPayConstant.TRADE_STATUS_FINISHED, trade_status)){
            editTrade.setOrderStatus(BizConstant.PAY_SUCCESS);
            resp.setCode(RespCode.SUCCESS.getCode()).setMsg(RespCode.SUCCESS.getDesc());
        }else{
            editTrade.setOrderStatus(BizConstant.PAY_FAIL);
            editTrade.setErorrCode(trade_status);
            resp.setCode(RespCode.ERROR.getCode()).setMsg(trade_status);
        }
        //更新订单状态
        log.debug("Update ali pay order status, model: {}", JsonUtil.bean2Json(editTrade));
        tradeMapper.editTrade(editTrade);

        //封装通知记录
        Notify notify = new Notify();
        long notifyId = IdGen.getId();
        notify.setNotifyId(notifyId);
        notify.setAppId(trade.getAppId());
        notify.setTradeId(trade.getTradeId());
        notify.setAppOrderId(trade.getAppOrderId());
        notify.setNotifyType(BizConstant.NOTIFY_TYPE_PAY);
        notify.setNotifyUrl(trade.getNotifyUrl());
        notify.setNotifyCount(1);
        notify.setNotifyStatus(BizConstant.NOTIFY_PENDING);
        log.debug("Add ali pay notify record, model: {}", notify);
        notifyMapper.addNotify(notify);

        //通知商户
        Map<String, Object> respMap = MapUtil.newHashMap();
        respMap.put("app_order_id", trade.getAppOrderId());
        respMap.put("total_fee", trade.getTotalFee());
        respMap.put("trade_id", trade.getTradeId());
        resp.setData(respMap);
        String json = JsonUtil.bean2Json(resp);
        log.debug("Ali pay notify mch, url: {}, req: {}", trade.getNotifyUrl(), json);
        notify(trade.getNotifyUrl(), json, ar -> {
            Notify dto = new Notify();
            dto.setNotifyId(notifyId);
            dto.setNotifyCount(1);
            dto.setNotifyStatus(BizConstant.NOTIFY_FAIL);
            if(ar.succeeded()){
                int code = ar.result().bodyAsJsonObject().getInteger("code");
                if(code == 0){
                    Trade tradeNotify = new Trade();
                    tradeNotify.setTradeId(trade.getTradeId());
                    tradeNotify.setOrderStatus(BizConstant.PAY_BIZ_COMPLETE);
                    tradeNotify.setFinishTime((int) (System.currentTimeMillis()/1000));
                    tradeNotify.setOldOrderStatus(BizConstant.PAY_SUCCESS); //原始订单状态为：1 支付成功
                    log.debug("Ali pay notification mch success, model: {}", JsonUtil.bean2Json(tradeNotify));
                    tradeMapper.editTrade(tradeNotify);

                    dto.setNotifyStatus(BizConstant.NOTIFY_SUCCESS);
                }
            }
            log.debug("Update ali pay notify record, model: {}", dto);
            notifyMapper.editNotify(dto);
        });
    }

    /**
     * 更新支付宝退款数据
     * @param map
     */
    private void editAliRefund(Map<String, String> map){
        Long out_trade_no = ConvertUtil.toLong(MapUtil.getStr(map, "out_trade_no"));
        String trade_status = MapUtil.getStr(map, "trade_status");
        long time_end = DateUtil.parse(MapUtil.getStr(map, "gmt_refund"), DatePattern.NORM_DATETIME_PATTERN).getTime() / 1000;
        Refund refund = refundMapper.getRefundByTradeId(out_trade_no);

        BaseResp<Map<String, Object>> resp = new BaseResp<>();
        if(refund != null){
            //组装退款
            Refund editRefund = new Refund();
            editRefund.setRefundId(refund.getRefundId());
            editRefund.setRefundTime((int) time_end);
            editRefund.setOldRefundStatus(BizConstant.WAIT_REFUND); //原始退款状态为：0 待退款

            if(StrUtil.equals(AliPayConstant.TRADE_STATUS_SUCCESS, trade_status)
                    && StrUtil.equals(AliPayConstant.TRADE_STATUS_FINISHED, trade_status)){
                editRefund.setRefundStatus(BizConstant.REFUND_SUCCESS);
                resp.setCode(RespCode.SUCCESS.getCode()).setMsg(RespCode.SUCCESS.getDesc());
            }else{
                editRefund.setRefundStatus(BizConstant.REFUND_FAIL);
                editRefund.setErorrCode(trade_status);
                resp.setCode(RespCode.ERROR.getCode()).setMsg(trade_status);
            }

            //更新退款订单状态
            log.debug("Update ali refund order status, model: {}", JsonUtil.bean2Json(editRefund));
            refundMapper.editRefund(editRefund);

            //封装通知记录
            Notify notify = new Notify();
            long notifyId = IdGen.getId();
            notify.setNotifyId(notifyId);
            notify.setAppId(refund.getAppId());
            notify.setTradeId(refund.getTradeId());
            notify.setAppOrderId(refund.getAppRefundId());
            notify.setNotifyType(BizConstant.NOTIFY_TYPE_REFUND);
            notify.setNotifyUrl(refund.getNotifyUrl());
            notify.setNotifyCount(1);
            notify.setNotifyStatus(BizConstant.NOTIFY_PENDING);
            log.debug("Add ali refund notify record, model: {}", JsonUtil.bean2Json(notify));
            notifyMapper.addNotify(notify);

            //通知商户
            Map<String, Object> respMap = MapUtil.newHashMap();
            respMap.put("app_refund_id", refund.getAppRefundId());
            respMap.put("refund_fee", refund.getRefundFee());
            respMap.put("trade_id", refund.getTradeId());
            respMap.put("refund_id", refund.getRefundId());
            resp.setData(respMap);
            String json = JsonUtil.bean2Json(resp);
            log.debug("Ali refund notify mch, url: {}, req: {}", refund.getNotifyUrl(), json);
            notify(refund.getNotifyUrl(), json, ar -> {
                Notify dto = new Notify();
                dto.setNotifyId(notifyId);
                dto.setNotifyCount(1);
                dto.setNotifyStatus(BizConstant.NOTIFY_FAIL);
                if(ar.succeeded()){
                    int code = ar.result().bodyAsJsonObject().getInteger("code");
                    if(code == 0){
                        Refund refundNotify = new Refund();
                        refundNotify.setTradeId(refund.getRefundId());
                        refundNotify.setRefundStatus(BizConstant.REFUND_BIZ_COMPLETE);
                        refundNotify.setFinishTime((int) (System.currentTimeMillis()/1000));
                        refundNotify.setOldRefundStatus(BizConstant.REFUND_SUCCESS); //原始退款状态为：1 退款成功
                        log.debug("Ali refund notification mch success, model: {}", JsonUtil.bean2Json(refundNotify));
                        refundMapper.editRefund(refundNotify);

                        dto.setNotifyStatus(BizConstant.NOTIFY_SUCCESS);
                    }
                }
                log.debug("Update ali refund notify record, model: {}", dto);
                notifyMapper.editNotify(dto);
            });
        }
    }

    private void notify(String notifyUrl, String json, Handler<AsyncResult<HttpResponse<Buffer>>> handler){
        webClient.postAbs(notifyUrl)
                .putHeader("content-type", "application/json")
                .timeout(timeout)
                .sendBuffer(Buffer.buffer(json.getBytes()), handler);
    }

    @PreDestroy
    public void destroy() {
        if (null != webClient) {
            webClient.close();
        }
    }
}
